﻿using System;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;

namespace CQRSFramework.RabbitMQ.Impl
{
    public class RabbitMQPersistentConnection : IRabbitMQPersistentConnection
    {
        private readonly IConnectionFactory _connectionFactory;
        private IConnection _connection;
        private readonly object obj = new object();
        bool _disposable;
        public RabbitMQPersistentConnection(IConnectionFactory connectionFactory)
        {
            _connectionFactory = (connectionFactory == null ? throw new ArgumentNullException(nameof(connectionFactory)) : connectionFactory);
        }

        public bool IsConnected
        {
            get
            {
                return _connection != null && _connection.IsOpen && !_disposable;
            }
        }

        public IModel CreateModel()
        {
            if (!IsConnected)
            {
                throw new InvalidOperationException("No RabbitMQ connections are available to perform this action");
            }
            return _connection.CreateModel();
        }

        public void Dispose()
        {
            if (_disposable) return;
            _disposable = true;
            try
            {
                _connection.Dispose();
            }
            catch (Exception ex)
            {

            }
        }

        public bool TryConnect()
        {
            lock (obj)
            {
                _connection = _connectionFactory.CreateConnection();
                if (IsConnected)
                {
                    _connection.ConnectionShutdown += _connection_ConnectionShutdown;
                    _connection.CallbackException += _connection_CallbackException;
                    _connection.ConnectionBlocked += _connection_ConnectionBlocked;
                    return true;
                }
                else
                {
                    return false;
                }
            }
        }

        private void _connection_CallbackException(object sender, CallbackExceptionEventArgs e)
        {
            TryConnect();
        }

        private void _connection_ConnectionBlocked(object sender, ConnectionBlockedEventArgs e)
        {
            TryConnect();
        }

        private void _connection_ConnectionShutdown(object sender, ShutdownEventArgs e)
        {
            TryConnect();
        }
    }
}
